#include "common/linkage.h"
#include "i386/kernel/io.h"
#include "i386/kernel/timer.h"
#include "i386/kernel/string.h"
#include "i386/mach-default/io_ports.h"
#include "i386/kernel/screen.h"
#include "i386/mach-default/do_timer.h"

#include "linux/timex.h"
#include "linux/jiffies.h"

static int count_p; /* counter in get_offset_pit() */

static int __init init_pit(char* override) {
  /*
  if (override[0] && strcmp(override, "pit", 3)) {
    puts("warning: clock= override failed. Default to PIT\n");
  }
  */
  count_p = LATCH;
  return 0;
}

static void mark_offset_pit(void) {
}

static u64 monotonic_clock_pit(void) {
  return 0;
}

static void delay_pit(u32 loops) {
  int d0;
  __asm__ __volatile__(
      "jmp  1f      \n\t"
      ".align 16    \n\t"
      "1: jmp 2f    \n\t"
      ".align 16    \n\t"
      "2: decl %0   \n\t"
      "jns  2b      \n\t"
      : "=&a"(d0)
      : "0"(loops));
}

static u32 get_offset_pit(void) {
  int count;
  // u32 flags;
  static u32 jiffies_p = 0;
  u32 jiffies_t;

  outb(0x00, PIT_MODE);
  count = inb(PIT_CH0);

  jiffies_t = jiffies;
  count |= inb(PIT_CH0) << 8;
  if (count > LATCH){
    outb(0x34, PIT_MODE);
    outb(LATCH & 0xff, PIT_CH0);
    outb((u8)(LATCH << 8), PIT_CH0);
    count = LATCH - 1;
  }

  if (jiffies_t == jiffies_p) {
    if (count > count_p) {
      count = do_timer_overflow(count);
    }
  } else {
    jiffies_p = jiffies_t;
  }

  count_p = count;
  count = ((LATCH - 1) - count) * TICK_SIZE;
  count = (count + LATCH / 2) / LATCH;

  return count;
}

struct timer_opts timer_pit = {
  .name = "pit",
  .mark_offset = mark_offset_pit,
  .get_offset = get_offset_pit,
  .monotonic_clock = monotonic_clock_pit,
  .delay = delay_pit,
};

struct init_timer_opts timer_pit_init = {
  .init = init_pit,
  .opts = &timer_pit,
};

void setup_pit_timer(void) {
  outb(0x34, PIT_MODE);
  udelay(10);
  outb(LATCH & 0xff, PIT_CH0);
  udelay(10);
  outb(LATCH >> 8, PIT_CH0);  
}

/*
static int timer_resume(struct sys_device* dev) {
  setup_pit_timer();
  return 0;
}

static struct sysdev_class timer_sysclass = {
  set_kset_name(""),
  .resume = timer_resume,
};

static struct sys_device devier_timer = {
  id = 0,
  .cls = &timer_sysclass,
};

static int __init init_timer_sysfs(void) {
  int error = sysdev_class_register(&timer_sysclass);
  if (!error) {
    error = sysdev_register(&device_timer);
  }
  return error;
}
*/
